<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Class Log
 *
 * Use:
 *
 *  1、require log class file;
 *
 *  2、init log file path;
 *
 *  3、call function to print log string.
 *
 * example：
 *
 *  require_once APPPATH . '/libraries/Log.php';
 *
 *  Log::init(array('log_path' => , 'log_file_name' => ,'log_level' => 'notice'));//log_level = debug|notice|warning
 *
 *  Log::debug($error_code, $log_arr);
 */
class Log
{

    private $_log_level;
    private $_log_id = 0;
    private $_log_path = '/var/log/si18n/';
    private $_log_file_name = 'si18n.log';
    private $_log_list = array();
    private $_push_notice = array();
    private static $_instance = false;

    /**
     * none
     * @var int
     */
    const LOG_NONE = 0x00;
    /**
     * Fatal
     * @var int
     */
    const LOG_FATAL = 0x01;
    /**
     * Error
     * @var int
     */
    const LOG_ERROR = 0x02;
    /**
     * Warning
     * @var int
     */
    const LOG_WARNING = 0x04;
    /**
     * notice
     * @var int
     */
    const LOG_NOTICE = 0x08;
    /**
     * debug
     * @var int
     */
    const LOG_DEBUG = 0x10;
    /**
     * all
     * @var int
     */
    const LOG_ALL = 0xFF;
    /**
     * level to text
     * @var array
     */
    private static $_arr_log_names = array(
        self::LOG_FATAL => 'FATAL',
        self::LOG_ERROR => 'ERROR',
        self::LOG_WARNING => 'WARNING',
        self::LOG_NOTICE => 'NOTICE',
        self::LOG_DEBUG => 'DEBUG',
    );

    /**
     * log_level_str to log_level_int
     * @val array
     */
    private static $_arr_log_level_str_to_int = array(
        'debug' => 31,//debug+notice+warning+error+fatal
        'notice' => 15,//notice+warning+error+fatal
        'warning' => 7,//warning+error+fatal
        'error' => 3,//error+fatal
        'fatal' => 1,//fatal
    );

    protected $_int_output_level = 0xFF;

    private function __construct()
    {
    }

    private function __clone()
    {
    }

    public static function get_instance()
    {
        if (self::$_instance === false) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    public static function init($init_arr = array())
    {
        $ins = self::get_instance();
        if (!empty($init_arr)) {
            if (is_array($init_arr)) {
                if (isset($init_arr['log_path'])) {
                    if (substr($init_arr['log_path'], strlen($init_arr['log_path']) - 1, 1) != '/') {
                        $ins->_log_path = $init_arr['log_path'] . '/';
                    } else {
                        $ins->_log_path = $init_arr['log_path'];
                    }
                }
                if (isset($init_arr['log_file_name'])) {
                    $ins->_log_file_name = $init_arr['log_file_name'];
                }
                if (isset($init_arr['log_level'])) {
                    $ins->_int_output_level = self::$_arr_log_level_str_to_int[$init_arr['log_level']];
                }
                if (empty($ins->_int_output_level)) {
                    $ins->_int_output_level = self::LOG_ALL;
                }
            }
        }
    }

    public static function set_log($log_file_name, $log_level = 'notice') {
        self::init(array('log_path' => '/var/log/scloud/', 'log_file_name' => $log_file_name,
            'log_level' => $log_level));
    }

    private function _check($int_log_level)
    {
        return (bool)($int_log_level & $this->_int_output_level);
    }

    public static function debug($error_code, $log_arr = array())
    {
        date_default_timezone_set('UTC');
        $ins = self::get_instance();
        $ins->_set_array(self::LOG_DEBUG, $error_code, $log_arr);
    }

    public static function notice($error_code, $log_arr = array())
    {
        date_default_timezone_set('UTC');
        $ins = self::get_instance();
        $ins->_set_array(self::LOG_NOTICE, $error_code, $log_arr);
    }

    public static function error($error_code, $log_arr = array())
    {
        date_default_timezone_set('UTC');
        $ins = self::get_instance();
        $ins->_set_array(self::LOG_ERROR, $error_code, $log_arr);
    }

    public static function warning($error_code, $log_arr = array())
    {
        date_default_timezone_set('UTC');
        $ins = self::get_instance();
        $ins->_set_array(self::LOG_WARNING, $error_code, $log_arr);
    }

    public static function fatal($error_code, $log_arr = array())
    {
        date_default_timezone_set('UTC');
        $ins = self::get_instance();
        $ins->_set_array(self::LOG_FATAL, $error_code, $log_arr);
        self::flush_log();
    }

    private function _set_array($int_log_level, $error_code, $log_data)
    {

        if (!$this->_check($int_log_level)) {
            return false;
        }
        $this->_get_log_id();
        $log_list = array();
        $log_list['log_time'] = date('Y-m-d H:i:s', strtotime("+8 hour"));
        $log_list['log_id'] = $this->_log_id;
        $str_log_type = 'NOTICE';
        if (isset(self::$_arr_log_names[$int_log_level])) {
            $str_log_type = self::$_arr_log_names[$int_log_level];
        }
        $log_list['log_level'] = $str_log_type;

        $log_list['err_code'] = $error_code;
        /*
        if (isset(ErrorCode::$codes[$error_code])) {
            $log_list['err_msg'] = ErrorCode::$codes[$error_code];
        }*/

        if (is_array($log_data)) {
            foreach ($log_data as $key => $value) {
                //$log_list[$key] = $value;
                $log_list[$key] = str_replace("\n", '', $value);
            }
        } else if (is_string($log_data)) {
            $log_list['err_str'] = $log_data;
        }


        $file_and_line = $this->_get_file_and_line();
        $log_list['filename'] = isset($file_and_line['file']) ? $file_and_line['file'] : '';
        $log_list['line'] = isset($file_and_line['line']) ? $file_and_line['line'] : '';


        if (is_cli()) {
            $log_list['action'] = $_SERVER['argv'][1] . "_" . $_SERVER['argv'][2];
            $log_list['user_agent'] = 'slightech_cli';
        } else {

            if ((defined('M_DEVELOPMENT') && M_DEVELOPMENT == ONLINE && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) || !defined('M_DEVELOPMENT')) {
                if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                    $log_list['user_ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
                }
            }


            $log_list['method'] = strtolower($_SERVER['REQUEST_METHOD']);
            $log_list['action'] = $_SERVER['REQUEST_URI'];
            $log_list['referer'] = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
            $log_list['user_agent'] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        }

        $this->_log_list[] = $log_list;
    }

    protected function _get_file_and_line($int_level = 0, $bool_full_file_path = false)
    {
        $arr_trace = debug_backtrace();
        $int_depth = 3 + $int_level;
        $int_trace_depth = count($arr_trace);
        if ($int_depth > $int_trace_depth)
            $int_depth = $int_trace_depth;
        $arr_ret = isset($arr_trace[$int_depth]) ? $arr_trace[$int_depth] : array();

        if (isset($arr_ret['file']) && !$bool_full_file_path) $arr_ret['file'] = basename($arr_ret['file']);
        return $arr_ret;
    }

    private function _split_array_to_str($arr = array())
    {
        $log_str = '';
        if (!empty($arr)) {
            foreach ($arr as $key => $value) {
                if (is_array($value)) {
                    $log_str .= $this->_split_array_to_str($value);
                } else {
                    $log_str .= $key . '[' . $value . '] ';
                }
            }
        }

        return $log_str;
    }

    public static function flush_log()
    {
        $ins = self::get_instance();
        $date = date('Ymd', strtotime("+8 hour"));
        if (!empty($ins->_log_list)) {
            foreach ($ins->_log_list as $key => $value) {
                if ($value['log_level'] != 'NOTICE') {
                    $log = $ins->_log_path . $ins->_log_file_name . '.wf.' . $date;
                    $log_str = $ins->_split_array_to_str($ins->_log_list[$key]);
                    $ins->_create_log($log);
                    $ins->_write_log($log, $log_str . "\n");
                } else {
                    $log = $ins->_log_path . $ins->_log_file_name . '.' . $date;
                    if (!empty($ins->_push_notice)) {
                        $ins->_log_list[$key][] = $ins->_push_notice;
                    }
                    $log_str = $ins->_split_array_to_str($ins->_log_list[$key]);
                    $ins->_create_log($log);
                    $ins->_write_log($log, $log_str . "\n");
                }

            }
        }
    }

    private function _write_log($log, $content)
    {
        $this->_create_log($log);
        file_put_contents($log, $content, FILE_APPEND);
    }

    private function _create_log($log)
    {
        if (!file_exists(dirname($log))) {
            mkdir(dirname($log), 0755);
        }
    }

    private function _get_log_id()
    {
        $request_time = gettimeofday();
        if ($this->_log_id <= 0) {
            $this->_log_id = intval($request_time['sec'] * 100000 + $request_time['usec'] / 10) & 0x7FFFFFFF;
        }
    }

    public static function push_notice($str_key, $str_value)
    {
        if (!empty($str_key) && !empty($str_value)) {
            $ins = self::get_instance();
            $ins->_push_notice[strval($str_key)] = strval($str_value);
        }
    }

    public function __destruct()
    {

        /*
         * --------------------------------------------------------------------
         * PRINT NOTICE LOG
         *
         *
         * push_notice($str_key, $str_value);
         * Log::notice(ERROR_CODE, LOG_ARRAY);
         * --------------------------------------------------------------------
         */
       // Log::notice(0, '');

        $this->flush_log();
    }
}


/**
 * override CodeIgniter's log_message()
 *
 * Error Logging Interface
 *
 * We use this as a simple mechanism to access the logging
 * class and send messages to be logged.
 *
 * @param    string    the error level: 'error', 'debug' or 'info'
 * @param    string    the error message
 * @return    void
 */
function log_message($level, $message)
{
    if ('warning' == strtolower($level)) {
        Log::warning(0, $message);
    } elseif ('error' == strtolower($level)) {
        Log::error(0, $message);
    } else {
        Log::debug(0, $message);
    }
}
